package cn.csust.net2.manager.server.dao.impl;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.annotation.Resource;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.mapping.Collection;

import cn.csust.net2.manager.client.ux.ColumnInfo;
import cn.csust.net2.manager.server.dao.BaseDAO;
import cn.csust.net2.manager.shared.po.Academy;
import cn.csust.net2.manager.shared.po.PO;
import cn.csust.net2.manager.shared.util.Constant;

import com.extjs.gxt.ui.client.Style.SortDir;
import com.extjs.gxt.ui.client.data.BaseListLoadConfig;
import com.extjs.gxt.ui.client.data.BasePagingLoadResult;
import com.extjs.gxt.ui.client.data.PagingLoadConfig;

/**
 * 
 * @author 韩忠金
 * 
 * @param <T>
 *           
 */
@SuppressWarnings("unchecked")
public abstract class BaseDAOImpl<T> implements BaseDAO<T> {
	@Override
	public List<T> findAll(BaseListLoadConfig config, String hql) {
		// TODO Auto-generated method stub
		String sort = config.getSortInfo().getSortField();
		SortDir i = config.getSortInfo().getSortDir();
		
		if(sort!=null){
			hql+= "order by "+sort;
			if(i==SortDir.ASC)
				hql+=" asc ";
			else if(i==SortDir.DESC)
				hql+=" desc ";			
		}
		Session session = this.getSessionFactory().getCurrentSession();
		Query query = session.createQuery(hql);
		
		return query.list();
	}

	// HIBERNATE的数据库session工厂
	@Resource(name = Constant.SESSION_FACTORY)
	private SessionFactory sessionFactory;

	/**
	 * 得到子类的特定类信息，便于在公共查询中的使用
	 * 附：这是泛型擦拭法使得Generic无法获取自己的Generic Type类型。
	 * 实际上BadClass<String>()实例化以后Class里面就不包括T的信息了，对于Class而言T已经被擦拭为Object
	 * ，而真正的T参数被转到使用T的方法（或者变量声明或者其它使用T的地方）里面（如果没有那就没有存根）
	 * ，所以无法反射到T的具体类别，也就无法得到T
	 * .class。而getGenericSuperclass(),getGenericInterfaces 是Generic继承的特例，
	 * 对于这种情况子类会保存父类的Generic参数类型，返回一个ParameterizedType，这时可以获取到父类的T.class了，
	 * 这也正是子类确定应该继承什么T的方法。
	 * @return
	 */
	private Class<T> getBaseClass() {
		
		Type genType = getClass().getGenericSuperclass();
		Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
		Class<T> entityClass = (Class<T>) params[0];
		return entityClass;
	}

	@Override
	public <G> void save(G t) {

		this.getSessionFactory().getCurrentSession().save(t);

	}

	@Override
	public <G> void delete(G t) {

		this.getSessionFactory().getCurrentSession().delete(t);

	}

	@Override
	public <G> void update(G t) {

		this.getSessionFactory().getCurrentSession().saveOrUpdate(t);

	}
	@Override
	public long counts(T t) {
		
		Criteria c = this.find(t,false);
		c.setProjection(Projections.rowCount());
		long count = (Long) c.uniqueResult();
		return count;
	}
	@Override
	public long countsByHQL(String hql) {
		Session session = this.getSessionFactory().getCurrentSession();
		Query query = session.createQuery(hql);
		return (long)query.list().size();
	}
	@Override
	public long countsBySQL(String sql) {
		Session session = this.getSessionFactory().getCurrentSession();
		Query query = session.createSQLQuery(sql);
		return (long)query.list().size();
	}
	@Override
	public BasePagingLoadResult<T> findByPaging(T t,PagingLoadConfig config) {
		
		Criteria c = this.find(t,false);
		c.setMaxResults(config.getLimit());
		c.setFirstResult(config.getOffset());
		String sort = config.getSortInfo().getSortField();
		SortDir i = config.getSortInfo().getSortDir();
		if(sort!=null){
			if(i==SortDir.ASC)
				c.addOrder(Order.asc(sort));
			else if(i==SortDir.DESC)
				c.addOrder(Order.desc(sort));			
		}
		List<T> list = c.list();
		return new BasePagingLoadResult<T>(list, config.getOffset(),(int) this.counts(t));

	}

	@Override
	public <G> BasePagingLoadResult<G> findAllByPaging(PagingLoadConfig config) {

		Criteria c = this.find(null,false);
		c.setMaxResults(config.getLimit());
		c.setFirstResult(config.getOffset());
		String sort = config.getSortInfo().getSortField();
		SortDir i = config.getSortInfo().getSortDir();
		if(sort!=null){
			if(i==SortDir.ASC)
				c.addOrder(Order.asc(sort));
			else if(i==SortDir.DESC)
				c.addOrder(Order.desc(sort));			
		}
		List<G> list = c.list();
		for(G tt :list){
		System.out.println(tt+"1");
		}
		return new BasePagingLoadResult<G>(list, config.getOffset(),(int) this.counts(null));

	}

	@Override
	public List<T> find(String key, String value) {
		
		String query = " from " + this.getBaseClass().getSimpleName()+ " as a where a." + key + " = " + value;
		Query q = this.getSessionFactory().getCurrentSession().createQuery(query);
		return q.list();
	}

	@Override
	public List<T> findAll() {
		
		Criteria c = this.find(null,false);
		return c.list();
	}

	@Override
	public List<T> findByExample(T t) {
		Session session = this.getSessionFactory().getCurrentSession();
		StringBuffer sb = new StringBuffer(" FROM "+this.getBaseClass().getSimpleName()+" WHERE 1 = 1 ");
		Class<T> cla = this.getBaseClass();
		Map<String ,Object> map = new HashMap<String,Object>();
		Field[] fields = cla.getDeclaredFields();
		try {
			for (Field f : fields) {
				f.setAccessible(true);
				//判断是不是集合的接口
				boolean b = true;
				for(Class<?> cl :f.getClass().getInterfaces()){
					if(cl.equals(Collection.class))
						b=false;
				}
				if(f.get(t)!=null&&(f.getType().equals(int.class))?Integer.parseInt(f.get(t).toString())!=0:1>0&&b&&!f.getName().equals("serialVersionUID")){
					if (f.getType().getSuperclass()!=null&&f.getType().getSuperclass().equals(PO.class)) {
						// 向关联字段中添加条件的对象
						map.put(f.getName(), f.get(t));
						sb.append(" AND "+f.getName()+" = :"+f.getName());
					}
				}
				else{
					continue;
				}
			}
			Query q = session.createQuery(sb.toString());
			Set<Entry<String, Object>> es = map.entrySet();
			Iterator<Entry<String, Object>> it = es.iterator();
			while(it.hasNext()){
				Entry<String, Object> entry = it.next();
				q.setEntity(entry.getKey(),entry.getValue());
			}
			return q.list();
		
		} catch (IllegalArgumentException e) {
		
			e.printStackTrace();
			
		} catch (IllegalAccessException e) {
		
			e.printStackTrace();
		
		}
		return null;

	}
	@Override
	public List<T> findListByExample(T t) {
			
		return  this.find(t,true).list();
	}
	@Override
	public T findUniqueByExample(T t) {
		
		return  (T) this.find(t,true).uniqueResult();

	}
	

	@Override
	public List<T> findListByCritera(DetachedCriteria criteria){
		Session session = this.getSessionFactory().getCurrentSession();
		List<T> list = criteria.getExecutableCriteria(session).list();
		return list;
		
	}
	@Override
	public Session getCurrentSession(){
		
		Session session = this.getSessionFactory().getCurrentSession();
		return session;
	}
	@Override
	public T findUniqueByCritera(DetachedCriteria criteria){
		Session session = this.getSessionFactory().getCurrentSession();
		T t = (T)criteria.getExecutableCriteria(session).uniqueResult();
		return t;
		
	}
	@Override
	public List<T> findListByHQL(String hql){
		
		return  this.getQueryByHql(hql).list();
		
	}
	@Override

	public T findUniqueHQL(String hql){
		
		return (T) this.getQueryByHql(hql).uniqueResult();
		
	}
	@Override
	public Criteria getThreadCriteria(){
		
		return this.find(null,false);
		
	}
	private Query getQueryByHql(String hql){
		
		Session session = this.getSessionFactory().getCurrentSession();
		return session.createQuery(hql);
	}
	/**
	 * 拿到当前线程的的高级条件对象
	 * isSuperclass对象说明当前的查询是否将子类对象进行向上转型
	 * @param t
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private Criteria find(T t,boolean isSuperclass) {
		
		Session session = this.getSessionFactory().getCurrentSession();
		Class cla = this.getBaseClass();
		if(isSuperclass&&cla.getSuperclass()!=null&&cla.getSuperclass()!=PO.class&&cla.getSuperclass().getSuperclass().equals(PO.class)){
			cla = cla.getSuperclass();
		};
//		if(cla.equals(Student.class)||cla.equals(Teacher.class))
//			cla = User.class;
		//如果参数为空则返回无条件查询对象
		/**
		 * 在简单的深度对象时候，反射耗费的时间基本为0，不耗费时间复杂度
		 * 
		 */
		
		if(t==null)
			return session.createCriteria(cla);
		Criteria c = session.createCriteria(cla).add(Example.create(t));
		Field[] fields = cla.getDeclaredFields();
long start = System.currentTimeMillis();
		try {
			for (Field f : fields) {
				f.setAccessible(true);
				//判断是不是集合的接口
				boolean b = false;
				for(Class<?> cl :f.getClass().getInterfaces()){
					if(cl.equals(Collection.class))
						b=false;
				}
				if(f.isAnnotationPresent(ColumnInfo.class)&&f.get(t)!=null){
					if(f.getAnnotation(ColumnInfo.class).isPrimary()){
						boolean bo = false;
						Type type = f.getType();
						if(type.equals(int.class)||type.equals(Integer.class))
							bo = (Integer)f.get(t)!=0;
						else if(type.equals(long.class)||type.equals(Long.class))
							bo = (Long)f.get(t)!=0;
						else if(type.equals(BigInteger.class))
							bo = !((BigInteger)f.get(t)).equals(new BigInteger("0"));
						if(bo){
							c = c.add(Restrictions.eq(f.getName(),f.get(t))) ;
						}
					}
				}
				if (f.get(t)!=null&&((f.getType().getSuperclass()!=null&&f.getType().getSuperclass().equals(PO.class))||b)) {	
						// 向关联字段中添加条件的对象
						Example e = Example.create( f.get(t));
						c = c.createCriteria(f.getName()).add(e);
						for (Field f2 : f.getType().getDeclaredFields()) {
							f2.setAccessible(true);
							if(f2.isAnnotationPresent(ColumnInfo.class)&&f2.get(f.get(t))!=null){
								if(f2.getAnnotation(ColumnInfo.class).isPrimary()){
									Type type = f2.getType();
									boolean bo = false;
									if(type.equals(int.class)||type.equals(Integer.class))
										bo = (Integer)f2.get(f.get(t))!=0;
									else if(type.equals(long.class)||type.equals(Long.class))
										bo = (Long)f2.get(f.get(t))!=0;
									else if(type.equals(BigInteger.class))
										bo = !((BigInteger)f2.get(f.get(t))).equals(new BigInteger("0"));
									if(bo){
										c = c.add(Restrictions.eq(f2.getName(),f2.get(f.get(t))));
									}
								}
							}
						}
					}
				else{
					
					continue;
				}
			}
long end = System.currentTimeMillis();
System.out.println("深度遍历对象生成条件查询耗费的时间为:"+(end-start)+"毫秒");
			return c;
		
		} catch (IllegalArgumentException e) {
		
			e.printStackTrace();
			
		} catch (IllegalAccessException e) {
		
			e.printStackTrace();
		
		}
		return null;

	}
	public <G> BasePagingLoadResult<G> findAllByPagingBySQL(PagingLoadConfig config,String sql,Class<?> cla){
		String sort = config.getSortInfo().getSortField();
		SortDir i = config.getSortInfo().getSortDir();
		long count = countsBySQL(sql);
		sql+= "order by "+sort;
		if(sort!=null){
			if(i==SortDir.ASC)
				sql+=" asc ";
			else if(i==SortDir.DESC)
				sql+=" desc ";			
		}
		Query query = getCurrentSession().createSQLQuery(sql).addEntity(cla);
		query.setMaxResults(config.getLimit());
		query.setFirstResult(config.getOffset());
		return  new BasePagingLoadResult<G>(query.list(), config.getOffset(),(int) count);
	}
	public <G> BasePagingLoadResult<G> findAllByPaging(PagingLoadConfig config,String hql,Map<String,Object> map){
		String sort = config.getSortInfo().getSortField();
		SortDir i = config.getSortInfo().getSortDir();
		long count = countsByHQL(hql,map);
		hql+= "order by "+sort;
		if(sort!=null){
			if(i==SortDir.ASC)
				hql+=" asc ";
			else if(i==SortDir.DESC)
				hql+=" desc ";			
		}
		Session session = this.getSessionFactory().getCurrentSession();
		Query query = session.createQuery(hql);
		parametersLoad(query,map);
		query.setMaxResults(config.getLimit());
		query.setFirstResult(config.getOffset());
		
		return  new BasePagingLoadResult<G>(query.list(), config.getOffset(),(int) count);
	}
	private void parametersLoad(Query query,Map<String,Object> map){
		Set<Entry<String, Object>> es = map.entrySet();
		Iterator<Entry<String, Object>> it = es.iterator();
		while(it.hasNext()){
			Entry<String, Object> en = it.next();
			String key = en.getKey();
			Object value = en.getValue();
			query.setParameter(key, value);
		}
	}
	private void parametersLoad(SQLQuery query,Map<String,Object> map){
		Set<Entry<String, Object>> es = map.entrySet();
		Iterator<Entry<String, Object>> it = es.iterator();
		while(it.hasNext()){
			Entry<String, Object> en = it.next();
			String key = en.getKey();
			Object value = en.getValue();
			query.setParameter(key, value);
		}
	}
	@Override
	public <G> BasePagingLoadResult<G> findAllByPaging(PagingLoadConfig config,String hql){
		String sort = config.getSortInfo().getSortField();
		SortDir i = config.getSortInfo().getSortDir();
		long count = countsByHQL(hql);
		hql+= "order by "+sort;
		if(sort!=null){
			if(i==SortDir.ASC)
				hql+=" asc ";
			else if(i==SortDir.DESC)
				hql+=" desc ";			
		}
		Session session = this.getSessionFactory().getCurrentSession();
		Query query = session.createQuery(hql);
		query.setMaxResults(config.getLimit());
		query.setFirstResult(config.getOffset());
		
		return  new BasePagingLoadResult<G>(query.list(), config.getOffset(),(int) count);
		
	}
	
	public <S>  List<S> findListByHQL_T(String hql){
		
		return (List<S>) this.getQueryByHql(hql).list();
	}
	public <S> S findUniqueHQL_T(String hql){
		
		return (S) this.getQueryByHql(hql).uniqueResult();
	}
	@SuppressWarnings("rawtypes")
	public   List findListByHQL_O(String hql){
		
		return this.getQueryByHql(hql).list();
	}
	public <G> List<G> findListBySQL(String sql){
		Session session = this.getSessionFactory().getCurrentSession();
		SQLQuery s = session.createSQLQuery(sql);
		return s.list();
	}
	public <G> G findUniqueBySQL(String sql){
		Session session = this.getSessionFactory().getCurrentSession();
		SQLQuery s =session.createSQLQuery(sql);
		return (G) s.uniqueResult();
	}
	/**
	 * HQL接口(使用Object参数，就是与T泛型无关)
	 * @param hql
	 * @return
	 */
	public Object findUniqueHQL_O(String hql){
		
		return this.getQueryByHql(hql).uniqueResult();
	}
	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}
	@Override
	public List<T> findAll(BaseListLoadConfig config, String hql,
			Map<String, Object> map) {
		// TODO Auto-generated method stub
		Criteria c = this.find(null,false);
		return c.list();
	}

	@Override
	public List<T> findListByHQL(String hql, Map<String, Object> map) {
		// TODO Auto-generated method stub
		Query query = hqlQueryLoad(hql,map);
		return  query.list();
	}

	@Override
	public T findUniqueHQL(String hql, Map<String, Object> map) {
		// TODO Auto-generated method stub
		Query query = hqlQueryLoad(hql,map);
		return (T) query.uniqueResult();
	}

	@Override
	public <S> List<S> findListByHQL_T(String hql, Map<String, Object> map) {
		// TODO Auto-generated method stub
		Query query = hqlQueryLoad(hql,map);
		return (List<S>) query.list();
	}

	@Override
	public <S> S findUniqueHQL_T(String hql, Map<String, Object> map) {
		// TODO Auto-generated method stub
		Query query = hqlQueryLoad(hql,map);
		return (S) query.uniqueResult();
	}

	@Override
	public List<Object> findListByHQL_O(String hql, Map<String, Object> map) {
		// TODO Auto-generated method stub
		Query query = hqlQueryLoad(hql,map);
		return query.list();
	}

	@Override
	public Object findUniqueHQL_O(String hql, Map<String, Object> map) {
		// TODO Auto-generated method stub
		Query query = hqlQueryLoad(hql,map);
		return query.uniqueResult();
	}

	@Override
	public long countsByHQL(String hql, Map<String, Object> map) {
		// TODO Auto-generated method stub
		Query query = hqlQueryLoad(hql,map);
		return (long)query.list().size();
	}
	public <G> BasePagingLoadResult<G> findAllByPagingBySQL(PagingLoadConfig config,String sql,Class<?> cla,Map<String,Object> map){
		String sort = config.getSortInfo().getSortField();
		SortDir i = config.getSortInfo().getSortDir();
		long count = countsBySQL(sql,map);
		sql+= "order by "+sort;
		if(sort!=null){
			if(i==SortDir.ASC)
				sql+=" asc ";
			else if(i==SortDir.DESC)
				sql+=" desc ";			
		}
		SQLQuery query =	sqlQueryLoad(sql,map);
		query.addEntity(cla);
		query.setMaxResults(config.getLimit());
		query.setFirstResult(config.getOffset());
		return  new BasePagingLoadResult<G>(query.list(), config.getOffset(),(int) count);
	}
	public <G> List<G> findListBySQL(String sql,Map<String,Object> map){
		SQLQuery s =sqlQueryLoad(sql,map);
		return s.list();
	}
	public <G> G findUniqueBySQL(String sql,Map<String,Object> map){
		SQLQuery s =sqlQueryLoad(sql,map);
		return (G) s.uniqueResult();
	}
	public long countsBySQL(String sql,Map<String,Object> map){
		SQLQuery query =sqlQueryLoad(sql,map);
		return (long)query.list().size();
	}
	private Query hqlQueryLoad(String hql, Map<String, Object> map){
		Query query = this.getQueryByHql(hql);
		parametersLoad(query,map);
		return query;
	}
	private SQLQuery sqlQueryLoad(String sql, Map<String, Object> map){
		Session session = this.getSessionFactory().getCurrentSession();
		SQLQuery query =session.createSQLQuery(sql);
		parametersLoad(query,map);
		return query;
	}
	
}
